#include <stdlib.h>

#include "list.h"
#include "init.h"
#include "debug.h"

NeighborListEntry** neighborListAllocations = 0;
int* remainingAllocs = 0;

NeighborList::NeighborList()
{
	head = NULL;
	tail = NULL;
	size = 0;
#ifdef INDIVIDUAL_LIST_LOCKS
	omp_init_lock(&lock);
#endif
}
NeighborList::~NeighborList()
{
	ClearData();
}

void NeighborList::ClearData()
{
#ifndef BULK_ALLOCATE_SHINGLES
	if(head)
		head = head->next;
	while(head)
	{
		NeighborListEntry *sto = head;
		head = head->next;
		delete(sto);
	}
#endif
}

void NeighborList::Push(unsigned long item)
{
#ifdef REPAIR_GRAPH
	for(NeighborListEntry *iter = StartIterator(); iter != NULL; iter=iter->next)
	{
		if(iter->value == item) return;
	}
#endif
	NeighborListEntry *ins = NeighborListEntry::AllocateNeighborListEntry(item);
#ifndef PARALLEL_ON_TRIALS
//#pragma omp critical (NeighborListPush)
#endif
	{ // Scope for the critical
		if(tail)
		{
			tail->next = ins;
			tail = tail->next;
		}
		else
			tail = ins;
		if(!head)
			head = tail;
		++size;
	}
}
void NeighborList::Push_NoThread(unsigned long item)
{
	// Identical as above, but without the lock.
	if(tail)
	{
		tail->next = NeighborListEntry::AllocateNeighborListEntry(item);
		tail = tail->next;
	}
	else
		tail = NeighborListEntry::AllocateNeighborListEntry(item);
	if(!head)
		head = tail;
	++size;
}
NeighborListEntry* NeighborList::StartIterator()
{
	return head;
}
unsigned long NeighborList::Head()
{
	return head->value;
}
unsigned long NeighborList::Tail()
{
	return tail->value;
}
unsigned int NeighborList::Size()
{
	return size;
}

NeighborListEntry* NeighborListEntry::AllocateNeighborListEntry(unsigned long item)
{
#ifndef BULK_ALLOCATE_SHINGLES
	return new NeighborListEntry(item);
#else
	int proc = omp_get_thread_num();
	if(remainingAllocs[proc] == 0)
	{
		neighborListAllocations[proc] = (NeighborListEntry*)calloc(BULK_ALLOCATE_SHINGLES, sizeof(NeighborListEntry));
		remainingAllocs[proc] = BULK_ALLOCATE_SHINGLES;
	}
	int index = BULK_ALLOCATE_SHINGLES - remainingAllocs[proc];
	NeighborListEntry *ret = &neighborListAllocations[proc][index];
	--remainingAllocs[proc];

	ret->value = item;
	ret->next = 0;

	return ret;
#endif
}
void NeighborListEntry::SetupAllocs()
{
	if(neighborListAllocations == 0)
	{
		int maxT = omp_get_max_threads();
		neighborListAllocations = (NeighborListEntry**)calloc(maxT, sizeof(NeighborListEntry*));
		remainingAllocs = (int*)calloc(maxT, sizeof(int));
		for(int i = 0; i < maxT; ++i)
		{
			neighborListAllocations[i] = 0;
			remainingAllocs[i] = 0;
		}
	}
}
NeighborListEntry::NeighborListEntry() { next = 0; value = 0; }
NeighborListEntry::NeighborListEntry(unsigned long item)
{
	next = 0;
	value = item;
}


ShingleList::ShingleList()
{
	head = NULL;
	tail = NULL;
	size = 0;
}
ShingleList::~ShingleList()
{
	while(head)
	{
		ShingleListEntry *sto = head;
		head = head->next;
		delete(sto);
	}
}
void ShingleList::Push(Shingle* item)
{
	if(tail)
	{
		tail->next = new ShingleListEntry(item);
		tail->next->prev = tail;
		tail = tail->next;
	}
	else
		tail = new ShingleListEntry(item);
	if(!head)
		head = tail;
	++size;
}
Shingle* ShingleList::Pop()
{
	if(tail)
	{
		Shingle *ret = tail->value;
		ShingleListEntry *del = tail;
		if(tail->prev)
		{
			tail = tail->prev;
		}
		else
		{
			head = NULL;
			tail = NULL;
		}
		size--;
		delete(del);
		return ret;
	}
	else
		return NULL;
}
ShingleListEntry* ShingleList::StartIterator()
{
	return head;
}
Shingle* ShingleList::Head()
{
	return head->value;
}
Shingle* ShingleList::Tail()
{
	return tail->value;
}
unsigned int ShingleList::Size()
{
	return size;
}
ShingleListEntry::ShingleListEntry(Shingle* item)
{
	prev = 0;
	next = 0;
	value = item;
}
